/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.controller.admin;

import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.ApiRequestMessage;
import cn.easyplatform.messages.request.SimpleRequestMessage;
import cn.easyplatform.messages.vos.admin.ApiVo;
import cn.easyplatform.messages.vos.admin.InputVo;
import cn.easyplatform.spi.service.AdminService;
import cn.easyplatform.spi.service.ApiService;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ServiceType;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.ext.cmez.CMeditor;
import cn.easyplatform.web.service.ServiceLocator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.*;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zkmax.zul.Timepicker;
import org.zkoss.zul.*;
import org.zkoss.zul.impl.DateTimeFormatInputElement;
import org.zkoss.zul.impl.FormatInputElement;
import org.zkoss.zul.impl.InputElement;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @Description:
 * @Since: 2.0.0 <br/>
 * @Date: Created in 2019/4/28 15:00
 * @Modified By:
 */
public class ApiController extends SelectorComposer<Component> implements EventListener {

    @Wire("grid#listbox")
    private Grid listbox;

    private List<ApiVo> model;

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        refresh();
    }

    @Listen("onOpen=#search")
    public void onSearch(Event evt) {
        OpenEvent oe = (OpenEvent) evt;
        String val = (String) oe.getValue();
        listbox.getRows().getChildren().clear();
        if (Strings.isBlank(val)) {
            refresh();
        } else {
            List<ApiVo> list = new ArrayList<>();
            for (ApiVo s : model) {
                if (s.getId().indexOf(val) >= 0 || s.getName().indexOf(val) >= 0)
                    list.add(s);
            }
            createList(list);
        }
    }

    private void createList(List<ApiVo> data) {
        StringBuilder sb = new StringBuilder();
        for (ApiVo av : data) {
            Row li = new Row();
            li.appendChild(new Label(av.getId()));
            li.appendChild(new Label(av.getName()));
            li.appendChild(new Label(av.getDesp()));
            li.appendChild(new Label(av.getRefId()));
            if (av.getInputs() != null) {
                sb.setLength(0);
                for (InputVo input : av.getInputs())
                    sb.append(input.getName()).append("(").append(input.getDesp()).append("),");
                sb.deleteCharAt(sb.length() - 1);
                li.appendChild(new Label(sb.toString()));
            } else
                li.appendChild(new Span());
            li.appendChild(new Label(av.getOutputs()));
            li.appendChild(new Label(av.getRunAsNodes()));
            li.appendChild(new Label(av.getRunAsRoles()));
            li.appendChild(new Label(av.getRunAsUsers()));
            Checkbox checkbox = new Checkbox();
            checkbox.setDisabled(true);
            checkbox.setChecked(av.isAnonymous());
            li.appendChild(checkbox);
            Button btn = new Button(Labels.getLabel("admin.project.model.test"));
            btn.addEventListener(Events.ON_CLICK, this);
            btn.setHflex("1");
            li.appendChild(btn);
            li.setValue(av);
            listbox.getRows().appendChild(li);
        }
    }

    private void refresh() {
        AdminService as = ServiceLocator.lookup(AdminService.class);
        IResponseMessage<?> resp = as.getData(new SimpleRequestMessage(ServiceType.API));
        if (resp.isSuccess()) {
            model = (List<ApiVo>) resp.getBody();
            createList(model);
        } else {
            MessageBox.showMessage(resp);
        }
    }

    @Override
    public void onEvent(Event event) throws Exception {
        ApiVo av = ((Row) event.getTarget().getParent()).getValue();
        new TestDialog(av).doModal();
    }

    private class TestDialog extends Window {

        CMeditor editor;

        TestDialog(ApiVo av) {
            this.setHeight("80%");
            this.setWidth("60%");
            this.setTitle(Labels.getLabel("admin.project.model.test") + ":" + av.getId() + "(" + av.getName() + ")");
            setClosable(true);
            setMaximizable(true);
            this.setPage(listbox.getPage());
            initComponents(av);
        }

        private void initComponents(ApiVo av) {
            Borderlayout layout = new Borderlayout();
            North north = new North();
            north.setTitle(Labels.getLabel("admin.api.inputs") + ":");
            north.setBorder("none");

            Grid grid = new Grid();
            grid.setSclass("z-form border-0 overflow-hidden");
            grid.appendChild(new Rows());
            Row row = new Row();
            row.setSclass("1,3");
            row.appendChild(new Label(Labels.getLabel("admin.api.page")));
            Hlayout hlayout = new Hlayout();
            hlayout.appendChild(new Label(Labels.getLabel("admin.api.page.start") + ":"));
            Intbox startPage = new Intbox(0);
            hlayout.appendChild(startPage);
            hlayout.appendChild(new Label(Labels.getLabel("admin.api.page.size") + ":"));
            Intbox pageSize = new Intbox(0);
            hlayout.appendChild(pageSize);
            row.appendChild(hlayout);
            grid.getRows().appendChild(row);
            row = null;

            if (av.getInputs() != null && !av.getInputs().isEmpty()) {
                row = new Row();
                row.setSpans("4");
                Separator separator = new Separator();
                row.appendChild(separator);
                grid.getRows().appendChild(row);
                row = null;

                for (InputVo input : av.getInputs()) {
                    if (row == null || row.getChildren().size() == 4) {
                        row = new Row();
                        row.setParent(grid.getRows());
                    }
                    row.appendChild(new Label(input.getDesp() + ":"));
                    switch (input.getType()) {
                        case CHAR:
                        case VARCHAR:
                        case CLOB:
                            Textbox textbox = new Textbox();
                            textbox.setHflex("1");
                            textbox.setId(input.getName());
                            textbox.setAccess(input.isRequired() + "");
                            row.appendChild(textbox);
                            break;
                        case INT:
                            Intbox intbox = new Intbox();
                            intbox.setHflex("1");
                            intbox.setId(input.getName());
                            intbox.setAccess(input.isRequired() + "");
                            intbox.setValue(0);
                            row.appendChild(intbox);
                            break;
                        case LONG:
                            Longbox longbox = new Longbox();
                            longbox.setHflex("1");
                            longbox.setId(input.getName());
                            longbox.setAccess(input.isRequired() + "");
                            longbox.setValue(0l);
                            row.appendChild(longbox);
                            break;
                        case NUMERIC:
                            Doublebox doublebox = new Doublebox();
                            doublebox.setHflex("1");
                            doublebox.setId(input.getName());
                            doublebox.setAccess(input.isRequired() + "");
                            doublebox.setValue(0);
                            if (input.getDecimal() > 0) {
                                doublebox.setFormat("###,##0." + StringUtils.repeat('0', input.getDecimal()));
                            } else doublebox.setFormat("###,##0");
                            row.appendChild(doublebox);
                            break;
                        case DATETIME:
                        case DATE:
                            Datebox datebox = new Datebox();
                            datebox.setHflex("1");
                            datebox.setId(input.getName());
                            datebox.setAccess(input.isRequired() + "");
                            if (input.getType() == FieldType.DATE) {
                                datebox.setFormat("yyyy-MM-dd");
                            } else datebox.setFormat("yyyy-MM-dd HH:mm:ss");
                            row.appendChild(datebox);
                            break;
                        case TIME:
                            Timepicker timepicker = new Timepicker();
                            timepicker.setHflex("1");
                            timepicker.setId(input.getName());
                            timepicker.setAccess(input.isRequired() + "");
                            row.appendChild(timepicker);
                            break;
                        case BOOLEAN:
                            Checkbox checkbox = new Checkbox();
                            checkbox.setId(input.getName());
                            row.appendChild(checkbox);
                            break;
                        case OBJECT:
                        case BLOB:
                            Button upload = new Button(Labels.getLabel("explorer.upload"));
                            upload.setHflex("1");
                            upload.setAccess(input.isRequired() + "");
                            upload.setId(input.getName());
                            upload.setUpload("true,maxsize=-1,native");
                            upload.addEventListener(Events.ON_UPLOAD, event -> {
                                UploadEvent evt = (UploadEvent) event;
                                upload.setAttribute("value", evt.getMedia().getByteData());
                            });
                            row.appendChild(upload);
                            break;
                    }
                }
                if (row != null) {
                    if (row.getChildren().size() == 4) {
                        row = new Row();
                        row.setSpans("4");
                        row.setAlign("center");
                        grid.getRows().appendChild(row);
                    } else
                        row.setSpans(StringUtils.repeat("1", ",", row.getChildren().size()) + "," + (4 - row.getChildren().size()));
                    Button test = new Button(Labels.getLabel("admin.project.model.test"));
                    test.setWidth("200px");
                    test.addEventListener(Events.ON_CLICK, event -> {
                        Map<String, Object> inputs = new HashMap<>();
                        for (Component c : getFellows()) {
                            if ("true".equals(c.getAccess())) {
                                if (c instanceof DateTimeFormatInputElement) {
                                    DateTimeFormatInputElement input = (DateTimeFormatInputElement) c;
                                    if (input.getValue() == null)
                                        throw new WrongValueException(input, Labels.getLabel("common.input.not.empty", new Object[]{((Label) c.getPreviousSibling()).getValue()}));
                                } else if (c instanceof FormatInputElement) {
                                } else if (c instanceof InputElement) {
                                    InputElement input = (InputElement) c;
                                    if (Strings.isBlank(input.getRawText()))
                                        throw new WrongValueException(input, Labels.getLabel("common.input.not.empty", new Object[]{((Label) c.getPreviousSibling()).getValue()}));
                                } else if (c instanceof Button && c.getAttribute("value") == null)
                                    throw new WrongValueException(c, Labels.getLabel("common.input.not.empty", new Object[]{((Label) c.getPreviousSibling()).getValue()}));
                            }
                            if (c instanceof InputElement)
                                inputs.put(c.getId(), ((InputElement) c).getRawValue());
                            if (c instanceof Checkbox)
                                inputs.put(c.getId(), ((Checkbox) c).isChecked());
                            if (c instanceof Button)
                                inputs.put(c.getId(), c.getAttribute("value"));
                        }
                        cn.easyplatform.messages.vos.ApiVo body = new cn.easyplatform.messages.vos.ApiVo();
                        body.setId(av.getId());
                        body.setData(inputs);
                        if (startPage.getValue() > 0 && pageSize.getValue() > 0) {
                            body.setStartNo(startPage.getValue());
                            body.setPageSize(pageSize.getValue());
                        }
                        ApiService as = ServiceLocator
                                .lookup(ApiService.class);
                        ApiRequestMessage req = new ApiRequestMessage(body);
                        IResponseMessage<?> resp = as.api(req);
                        Map<String, Object> data = new HashMap<>();
                        data.put("code", resp.getCode());
                        data.put("data", resp.getBody());
                        editor.setValue(JSON.toJSONString(data, SerializerFeature.PrettyFormat));
                        editor.formatAll();
                    });
                    row.appendChild(test);
                }
            }
            north.appendChild(grid);
            layout.appendChild(north);

            Center center = new Center();
            center.setBorder("none");
            center.setTitle(Labels.getLabel("admin.api.test.result") + ":");
            editor = new CMeditor();
            editor.setMode("json");
            editor.setReadonly(true);
            editor.setHflex("1");
            editor.setVflex("1");
            editor.setLineNumbers(false);
            editor.setLineWrapping(false);
            editor.setAutocomplete(false);
            editor.setParent(center);
            layout.appendChild(center);

            layout.setParent(this);
        }
    }
}
